/** @file    HeyuBinding.cpp
 *  @author  Alessandro Polo
 *  @version $Id: HeyuBinding.cpp 2884 2010-08-10 00:31:45Z alex $
 *  @brief
 * File containing methods for the HeyuBinding class.
 * The header for this class can be found in HeyuBinding.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include "HeyuBinding.h"

 #include <core/Utilities.h>
 #include <sstream>

 // pipe and process control
 #include <signal.h> 
 
char heyumonitor_Started[] = "Monitor started\0";
char heyumonitor_Reconfigured[] = "Monitor reconfiguration";
char heyumonitor_event_src_sndc[] = "sndc"; // heyu command line;
char heyumonitor_event_src_snds[] = "snds"; // heyu2 command line dall'interno di uno script;
char heyumonitor_event_src_sndm[] = "sndm"; // macro temporizzata;
char heyumonitor_event_src_sndt[] = "sndt"; // macro scatenata da uno specifico evento.
char heyumonitor_event_src_rcvi[] = "rcvi"; // ricevuti sulla linea elettrica.
char heyumonitor_event_addr_unit[] = "addr unit";
char heyumonitor_event_func[] = "func";

using namespace std;
using namespace wosh;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

HeyuBinding::HeyuBinding( BundleGeneric& bundle )
	: BundleGenericWorker(bundle) {
	this->driverX10Listener = NULL;
	this->housecodes = "ABCDEFGHIJKLMOP";
	this->heyuState = HeyuBinding::STATE_UNKNOWN;

	this->refreshFreq = 1000; //ms
	this->aliasReloadFreq = 300; // #times of refreshFreq (=multiplier)

	this->heyuBin = "heyu";
	this->heyuVersion = "";
	this->heyuConfigFile = "";
	this->heyuPowerLineInt = "";

	this->monitor_process_id = 0;

	allocUnits();

	clearCache();
 }

HeyuBinding::~HeyuBinding() {
	if ( isThreadRunning() ) {
		quitThread(15000);
	 }
	this->devices.free();
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void HeyuBinding::allocUnits() {
	Log(LOG_DEBUG, ":allocUnits() : Creating 255 Devices.." );
	for ( unsigned short i=0; i<16; i++ ) {
		for ( unsigned short uc=1; uc<17; uc++ ) {
			codeX10 code;
			code.housecode = i;
			code.unitcode = uc;
			DeviceX10* dev = new DeviceX10(code);
			//dev->setParentObject( this->parentRef );
			//dev->setPermission( this->parentRef->getPermission() );
			dev->setPermission( Permission( Permission::RWX ) );
			this->devices.push_back(dev);
		 }
	 }
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT HeyuBinding::clearCache() {
	Log(LOG_VERBOSE, ":clearCache() : Clearing Data.." );
	this->configMux.lockForWrite();
	this->heyuState = HeyuBinding::STATE_UNKNOWN;
	this->heyuVersion = "";
	this->heyuConfigFile = "";
	this->heyuPowerLineInt = "";
	this->configMux.unLock();
	int zero_state = -1;
	tDeviceX10List::Iterator it;
	const tDeviceX10List::ConstIterator it_end = this->devices.end();
	for ( it=this->devices.begin(); it!=it_end; ++it ) {
		if ( *it == NULL ) continue;
		(*it)->setX10State( zero_state );
	 }
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////// THREAD RUN

bool HeyuBinding::initializingThread() {
	//clear the internal cache (map)
	clearCache();
	if ( this->heyuBin == "" ) {
		Log(LOG_CRITICAL, ":runThread() FAILED : HeyuBin is Empty!" );
		return false;
	 }
	// update state, ensure engine is running, if not try to start it
	if ( !heyuEngineCheckState() ) {
		Log(LOG_WARNING, ":runThread(): Heyu engine is stopped, trying to start.." );
		WRESULT ret = heyuEngineStart();
		if ( WFAILED(ret) ) {
		 }
		heyuEngineCheckState();
	 }
	// if engine is not working, this is nonsense
	if ( this->heyuState != HeyuBinding::STATE_RUNNING ) {
		Log(LOG_CRITICAL, ":runThread() FAILED : Heyu Engine is not running. Exiting Thread." );
		return false;
	 }
	heyuGetInfo();
	return true;
 }

void HeyuBinding::runThread() {
	Log(LOG_VERBOSE, ":runThread(): Starting loop.." );
	updateAll(false);
	watchMonitor(this->running);
	Log(LOG_VERBOSE, ":runThread(): Exited loop.." );
 }

WRESULT HeyuBinding::quitThread( unsigned long timeout ) {
	///@todo use fork and do that better..
	if ( this->monitor_process_id > 0 ) {
		this->running = false;
		ThreadImpl::sleepForMSec(100);
		Log(LOG_VERBOSE, ":quitThread(): Sending CTRL-C.." );
		kill(this->monitor_process_id, SIGINT); // CTRL-C to the process :)
		run_x10_command("restart");
	 }
	return ThreadImpl::quitThread(timeout);
 }

////////////////////////////////////////////////////////////////////////////////////////////////// THREAD RUN
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////// EVENTS

WRESULT HeyuBinding::watchMonitor( bool& stop_request ) {
	if ( this->heyuBin == "" ) return WRET_ERR_PARAM;
	string cmd = this->heyuBin + " monitor";
	const int bufLength = 511;
	char inbuf[bufLength];
	memset(inbuf,'\0',bufLength);
	char outbuf[bufLength];
	memset(outbuf,'\0',bufLength);
	Log(LOG_DEBUG, ":watchMonitor() : open pipe for '%s'", cmd.c_str() );
	FILE* inpipe = popen(cmd.c_str(), "r");
	if ( !inpipe ) {
		this->monitor_process_id = 0;
		Log(LOG_CRITICAL, ":watchMonitor() : couldn't open pipe" );
		return WRET_ERR_INTERNAL;
	 }
	this->monitor_process_id = fileno(inpipe);
	Log(LOG_VERBOSE, ":watchMonitor() : opened pipe fileno()=%ld, entering read loop", this->monitor_process_id );
	while ( stop_request && fgets(inbuf, sizeof(inbuf), inpipe) != NULL ) { 
		//setThreadAlive();
		parseMonitorLine(inbuf, strlen(inbuf), outbuf );
	 }
	Log(LOG_DEBUG, ":watchMonitor() : closing pipe for '%s'", cmd.c_str() );
	pclose( inpipe );
	return WRET_OK;
 }

void HeyuBinding::parseMonitorLine( const char* data, int size, char* iout ) {
	// supported format(s) :
	//		0   4   8      16      24       32      40 44

	//		10/05 12:48:12  Monitor Started
	//		10/05 12:49:32  sndc addr unit       4 : hu C4  (light_office_desktop)
	//		10/05 12:49:46  sndc func          Off : hc C
	//		10/05 12:54:22  Monitor reconfiguration
	//		10/05 12:59:56  rcvi addr unit       4 : hu H4  (motion_sensor_stairs_up)
	//		10/05 12:59:57  rcvi func           On : hc H
	//		10/05 12:59:57  rcvi func          Dim : hc H bim %12 [23]
	//		10/05 12:59:57  rcvi func       Bright : hc H bright %03 [102]
	int index = 16;
	if ( index >= size ) {
		printf( ":parseMonitorLine() '%s', %d bytes", data, size);
		memset(iout, 0, 3);
		return;
	 }
	//printf( "===>%s", data+index);
	if ( strncmp(heyumonitor_Started, data+index, strlen(heyumonitor_Started)) == 0 ) {
		memset(iout, 0, 3); // clean intermediate buffer
		Log(LOG_INFO, ":parseMonitorLine() : Monitor Started!" );
	 }
	else if ( strncmp(heyumonitor_Reconfigured, data+index, strlen(heyumonitor_Reconfigured)) == 0 ) {
		memset(iout, 0, 3); // clean intermediate buffer
		Log(LOG_INFO, ":parseMonitorLine() : Monitor ReConfiguration!" );
		if ( this->driverX10Listener != NULL )
			this->driverX10Listener->driver_event( HeyuBinding::ENGINE_RECONFIGURATION, this );
	 }
	else { // its an event! (events are defined by TWO following lines!)
		EVENT_SOURCE source = SOURCE_UNKNOWN;
		if ( strncmp(heyumonitor_event_src_sndc, data+index, strlen(heyumonitor_event_src_sndc)) == 0 ) {
			source = SOURCE_COMMANDLINE; index += strlen(heyumonitor_event_src_sndc);
		 }
		else if ( strncmp(heyumonitor_event_src_snds, data+index, strlen(heyumonitor_event_src_snds)) == 0 ) {
			source = SOURCE_SCRIPT; index += strlen(heyumonitor_event_src_snds);
		 }
		else if ( strncmp(heyumonitor_event_src_sndm, data+index, strlen(heyumonitor_event_src_sndm)) == 0 ) {
			source = SOURCE_MACRO; index += strlen(heyumonitor_event_src_sndm);
		 }
		else if ( strncmp(heyumonitor_event_src_sndt, data+index, strlen(heyumonitor_event_src_sndt)) == 0 ) {
			source = SOURCE_MACROEVENT; index += strlen(heyumonitor_event_src_sndt);
		 }
		else if ( strncmp(heyumonitor_event_src_rcvi, data+index, strlen(heyumonitor_event_src_rcvi)) == 0 ) {
			source = SOURCE_POWERLINE; index += strlen(heyumonitor_event_src_rcvi);
		 }
		index += 1; // space after event SOURCE {sndc,rcvi}
		if ( strncmp(heyumonitor_event_addr_unit, data+index, strlen(heyumonitor_event_addr_unit)) == 0 ) {
			if ( index+3 >= size ) {
				printf( ":parseMonitorLine() '%s', %d bytes", data, size);
				Log(LOG_VERBOSE, ":parseMonitorLine() : SIZE ERROR '%s', [%d+3 >= %d bytes ]", data, index, size);
				memset(iout, 0, 3);
				return;
			 }
			memset(iout, 0, 3); // clean intermediate buffer
			memcpy(iout, data+44, 3);
			if ( iout[2] == ' ' )
				iout[2] = '\0';
			Log(LOG_VERBOSE, ":parseMonitorLine() : Event SRC=%d ADDR=%s", (int)source, iout );
		 }
		else if ( strncmp(heyumonitor_event_func, data+index, strlen(heyumonitor_event_func)) == 0 ) {
			index += strlen(heyumonitor_event_func);
			index += 1; // space
			char buffer[32];
			memset(buffer, 0, 32);
			memcpy(buffer, data+index, 13);
			int i=-1;
			while( i < 31 && buffer[++i] == ' ' )
				buffer[i] = 0;
			DeviceX10::MODULE_ACTION event = DeviceX10::EVENT_UNKNOWN;
			int arg_value = -1000;
			if ( strncmp("On", buffer+i, 2 ) == 0 ) {
				event = DeviceX10::EVENT_ON;
				arg_value = 100;
			 }
			else if ( strncmp("Off", buffer+i, 3 ) == 0 ) {
				event = DeviceX10::EVENT_OFF;
				arg_value = 0;
			 }
			else if ( strncmp("Dim", buffer+i, 3 ) == 0 ) {
				event = DeviceX10::EVENT_DIM;
				arg_value = -5; ///@bug implement here
				//buffer+i+13
			 }
			else if ( strncmp("Bright", buffer+i, 6 ) == 0 ) {
				event = DeviceX10::EVENT_BRIGHT;
				arg_value = 5; ///@bug implement here
				//buffer+i+13
			 }
			// DON't clean intermediate buffer because 2 following event strip the ADDR-.. line
			//memset(iout, 0, 3);
			int idev = x10codeStr2int( iout );
			if ( idev < 0 || idev > 255 ) {
				Log(LOG_WARNING, ":parseMonitorLine() : ERROR Event SRC=%d FUNC=%s (Ev=%d)[Arg=%d] {INVALID Dev=%d}[iOut=%s]", (int)source, buffer+i, (short)event, arg_value, idev, iout );
			 }
			else {
				Log(LOG_VERBOSE, ":parseMonitorLine() : Event SRC=%d FUNC=%s (%d)[S=%d] {Dev=%d}[iOut=%s]", (int)source, buffer+i, (short)event, arg_value, idev, iout );
			 }
			if ( this->driverX10Listener != NULL )
				this->driverX10Listener->device_event( this->devices.itemAt(idev), event, arg_value, (int)source, this );
		 }
		else {
			memset(iout, 0, 3); // clean intermediate buffer
			Log(LOG_VERBOSE, ":parseMonitorLine() : iOut=%s; Event SRC=%d UNKNOWN=%s", iout, (int)source, data+index );
		 }
	 }
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT HeyuBinding::updateAll( bool raise_event ) {
	if ( this->heyuBin == "" ) return WRET_ERR_PARAM;
	std::vector<std::string> results;
	runCommandWithRet( this->heyuBin + " show dim", results );
	std::string ignore = "";
	int buf = -1;
	int buf_old = -1;
	if ( results.size() < 19 ) {
		Log(LOG_CRITICAL, ":updateAll() : Error, invalid results [%d lines]", results.size() );
		return WRET_ERR_INTERNAL;
	 }
	//remove headers
	results.erase( results.begin() ); // .. Percent ..
	results.erase( results.begin() ); //  Unit: ..
	results.erase( results.begin() ); // HCode ..
	DeviceX10* dev = NULL;
	for ( int i=0; i<16; i++ ) {
		std::istringstream tmp( results[i] );
		tmp >> ignore;
		for ( int j=1; j<17; j++ ) {
			dev = this->devices.itemAt(i*16+j-1);
			if ( dev == NULL ) continue;
			tmp >> buf;
			buf_old = dev->getX10State();
			if ( buf_old != buf ) {
				if ( raise_event && this->driverX10Listener != NULL )
					this->driverX10Listener->device_event( dev, DeviceX10::EVENT_UNKNOWN, buf, (int)HeyuBinding::SOURCE_CACHE, this );
				dev->setX10State( buf );
			 }
		 }
	 }
	return 0;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

int HeyuBinding::run_x10_command( const std::string& cmdline ) {
	if ( this->heyuBin == "" ) return -1001;
	std::string cmd = this->heyuBin + " " + cmdline;
	int ret = system( cmd.c_str() );
	Log(LOG_VERBOSE, ":run_x10_command() : '%s' returned %d", cmdline.c_str(), ret );
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT HeyuBinding::runCommandWithRet( const std::string& command, std::vector<std::string>& outputLines ) {
	FILE* inpipe = NULL;
	const int bufLength = 511;
	char inbuf[bufLength];
	memset(inbuf,'\0',bufLength);
	outputLines.clear();
	string cmd = command + " 2>&1";
	inpipe = popen(cmd.c_str(), "r");
	if (!inpipe) {
		Log(LOG_CRITICAL, ":runCommandWithRet() : couldn't open pipe" );
		return WRET_ERR_INTERNAL;
	 }
	while ( fgets(inbuf, sizeof(inbuf), inpipe) != NULL ) { 
		if ( inbuf[ strlen(inbuf)-1 ] == '\n' )
			inbuf[ strlen(inbuf)-1 ] = '\0';
		if ( inbuf[ strlen(inbuf)-1 ] == '\r' )
			inbuf[ strlen(inbuf)-1 ] = '\0';
		outputLines.push_back( string(inbuf) );
	 }
	pclose( inpipe );
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////// HEYU COMMON 

WRESULT HeyuBinding::heyuEngineStart() {
	int ret = run_x10_command( "engine" );
	if ( ret == 0 || ret == 256 )
		return WRET_OK;
	Log(LOG_WARNING, ":heyuEngineStart() '- engine' returned [%d]", ret );
	return WRET_ERR_INTERNAL;
 }

WRESULT HeyuBinding::heyuEngineStop() {
	int ret = run_x10_command( "stop" );
	if ( ret == 0 )
		return WRET_OK;
	Log(LOG_WARNING, ":heyuEngineStart() '- engine' returned [%d]", ret );
	return WRET_ERR_INTERNAL;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool HeyuBinding::heyuEngineCheckState() {
	this->heyuState = HeyuBinding::STATE_UNKNOWN;
	if ( this->heyuBin == "" ) return false;
	std::vector<std::string> outputLines;
	std::string stateStr = "-1";
	runCommandWithRet( this->heyuBin + " enginestate", outputLines );
	if ( outputLines.size() > 0 ) // rip endline
		stateStr = outputLines[0];
	if ( stateStr == "0" )		this->heyuState = HeyuBinding::STATE_STOPPED;
	else if ( stateStr == "1" )	this->heyuState = HeyuBinding::STATE_RUNNING;
	else						this->heyuState = HeyuBinding::STATE_UNKNOWN;
	Log(LOG_VERBOSE, ":checkState() '%s' [%d]", stateStr.c_str(), (int)this->heyuState );
	if ( this->heyuState == HeyuBinding::STATE_RUNNING )
		return true;
	return false;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT HeyuBinding::heyuGetInfo() {
	if ( this->heyuBin == "" ) return WRET_ERR_PARAM;
	std::vector<std::string> results;
	runCommandWithRet( this->heyuBin + " info", results );
	this->heyuVersion = "";
	this->heyuConfigFile = "";
	this->heyuPowerLineInt = "";
	if ( results.size() > 0 && results[0].size() > 13 )
		this->heyuVersion = results[0].substr( 13 );
	Log(LOG_VERBOSE, ":heyuGetInfo() Ver = %s", this->heyuVersion.c_str() );
	if ( results.size() > 1 && results[1].size() > 17 )
		this->heyuConfigFile = results[1].substr( 17 );
	Log(LOG_VERBOSE, ":heyuGetInfo() ConfigFile = %s", this->heyuConfigFile.c_str() );
	if ( results.size() > 2 && results[2].size() > 22 )
		this->heyuPowerLineInt = results[2].substr( 22 );
	Log(LOG_VERBOSE, ":heyuGetInfo() PowerLineInt = %s", this->heyuPowerLineInt.c_str() );
	results.clear();
	std::string armedStr = "unknown";
	runCommandWithRet( this->heyuBin + " show armed", results );
	if ( results.size() > 0 ) // rip endline
		armedStr = results[0];
	this->heyuArmedState = armedStr;
	Log(LOG_VERBOSE, ":heyuGetInfo() ArmedState = %s", this->heyuArmedState.c_str() );
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
WRESULT HeyuBinding::heyuGetAliases()
 {
	if ( this->heyuBin == "" ) return WRET_ERR_PARAM;
	std::vector<std::string> results;
	runCommandWithRet( this->heyuBin + " show aliases", results );
	Log(LOG_VERBOSE, ":heyuGetAliases()" );
	std::string alias = "";
	std::string code = "";
	std::string ignore = "";
	std::string type = "";
	int loaded = 0;
	std::vector<std::string>::const_iterator it;
	std::vector<std::string>::const_iterator it_s = results.begin();
	if ( *it_s == " [Aliases]" )
		it_s++;
	else
		Log(LOG_WARNING, ":heyuGetAliases() : [Aliases] keyword not found!" );
	DeviceX10* dev = NULL;
	for ( it=it_s; it!=results.end(); it++ ) {
		std::istringstream tmp( *it );
		tmp >> ignore;
		if ( ignore != "alias" ) {
			Log(LOG_WARNING, ":heyuGetAliases() : Alias keyword not found, found %s", ignore.c_str() );
		 }
		alias = ""; tmp >> alias;
		code = ""; tmp >> code;
		type = ""; tmp >> type;
		if ( alias.size() == 0 ) continue;
		if ( code.size() == 0 ) continue;
		if ( code.find(",") != string::npos ) continue;
		unsigned short x10c = string2codeX10num(code);
		if ( x10c < 1 || x10c > 256 ) {
			Log(LOG_WARNING, ":heyuGetAliases() Dev#%d OUT OF RANGE: Alias '%s' => '%s' [%s]", x10c, alias.c_str(), code.c_str(), type.c_str() );
			continue;
		 }
		dev = this->devices.itemAt(x10c-1);
		if ( dev == NULL ) continue;
		dev->setX10Alias( alias );
		++loaded;
		Log(LOG_VERBOSE, ":heyuGetAliases() Alias '%s' => '%s' [%s]", alias.c_str(), code.c_str(), type.c_str() );
		DeviceX10::MODULE_TYPE ntype = DeviceX10::getX10ModuleNameFromString( type );
		dev->setModuleType( ntype );
		if ( dev->isSwitch() )
			dev->getInterfaces().add( wosh::interfaces::devices::Switch::bundleType() );
		else if ( dev->isDimmer() )
			dev->getInterfaces().add( wosh::interfaces::devices::Dimmer::bundleType() );
		else if ( dev->isSensor() ) {
			if ( alias.find("sensor_m") == 0 )
				dev->getInterfaces().add( wosh::interfaces::devices::SensorMotion::bundleType() );
			else if ( alias.find("sensor_l") == 0 )
				dev->getInterfaces().add( wosh::interfaces::devices::SensorLight::bundleType() );
			else {
				Log(LOG_WARNING, ":heyuGetAliases() Alias '%s' is Unknown Sensor [%s]", alias.c_str(), type.c_str() );
			 }
		 }
		else {
			Log(LOG_WARNING, ":heyuGetAliases() Alias '%s' is Unknown Type [%s] => defaulting to Switch", alias.c_str(), type.c_str() );
			dev->getInterfaces().add( wosh::interfaces::devices::Switch::bundleType() );
		 }
	 }
	Log(LOG_VERBOSE, ":heyuGetAliases() Loaded %d aliases", loaded );
	return WRET_OK;
 }

///////////////////////////////////////////////////////////////////////////////////////////////// HEYU COMMON 
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT HeyuBinding::turnUnitOn( const std::string& x10code ) {
	int ret = run_x10_command( "on " + x10code );
	if ( ret != 0 ) return WRET_ERR_INTERNAL;
	return WRET_OK;
 }

WRESULT HeyuBinding::turnUnitOff( const std::string& x10code ) {
	int ret = run_x10_command( "off " + x10code );
	if ( ret != 0 ) return WRET_ERR_INTERNAL;
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT HeyuBinding::setUnitValue( const std::string& x10code, unsigned int value ) {
	WRESULT ret = turnUnitOff(x10code);
	if ( WFAILED(ret) ) {
		Log(LOG_VERBOSE, "*:setUnitValue(%s) Error calling turnUnitOff", x10code.c_str() );
		return WRET_ERR_INTERNAL;
	 }
	return setUnitValueOffset(x10code, value);
 }

WRESULT HeyuBinding::setUnitValueOffset( const std::string& x10code, int value ) {
	std::string cmd;
	if ( value < 0 )
		cmd = Utilities::format("dim %s %d", x10code.c_str(),-value);
	else if ( value > 0 )
		cmd = Utilities::format("bright %s %d", x10code.c_str(),value);
	else {
		cmd = "off " + x10code;
		Log(LOG_VERBOSE, "*:setUnitValueOffset(%s) Interpreting '%d' as OFF", x10code.c_str(), value );
	 }
	int ret = run_x10_command( cmd );
	if ( ret != 0 ) return WRET_ERR_INTERNAL;
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void HeyuBinding::setHeyuBinary( const std::string& value ) {
	MutexLockerWrite mL(&configMux);
	this->heyuBin = value;
 }

void HeyuBinding::setRefreshFrequency( unsigned int value_ms ) {
	MutexLockerWrite mL(&configMux);
	this->refreshFreq = value_ms;
 }

void HeyuBinding::setAutoReloadAliases( int value_seconds  ) {
	MutexLockerWrite mL(&configMux);
	this->aliasReloadFreq = value_seconds;
 }

void HeyuBinding::setHeyuBindingListener( IHeyuBindingListener* listener ) {
	this->driverX10Listener = listener;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

IHeyuBindingListener* HeyuBinding::getHeyuBindingListener() {
	return this->driverX10Listener;
 }

std::string HeyuBinding::getHeyuBinary() const {
	MutexLockerRead mL(&configMux);
	return this->heyuBin;
 }

std::string HeyuBinding::getHeyuVersion() const {
	MutexLockerRead mL(&configMux);
	return this->heyuVersion;
 }

std::string HeyuBinding::getHeyuConfigFile() const {
	MutexLockerRead mL(&configMux);
	return this->heyuConfigFile;
 }

std::string HeyuBinding::getHeyuPowerLineInt() const {
	MutexLockerRead mL(&configMux);
	return this->heyuPowerLineInt;
 }

std::string HeyuBinding::getHeyuArmedState() const {
	MutexLockerRead mL(&configMux);
	return this->heyuArmedState;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
